package org.dicr.gui;

import javax.swing.*;

/**
 * <p>
 * Title: DiJaWo
 * </p>
 * <p>
 * Description: Dicr Java Utilites
 * </p>
 * <p>
 * Copyright: (C) by Dicr 2002
 * </p>
 * <p>
 * Company: Dicr
 * </p>
 * 
 * @author Igor A Tarasov me@dicr.dn.ua
 * @version YYMMDD This is the 3rd version of SwingWorker (also known as SwingWorker 3), an abstract class that you
 *          subclass to perform GUI-related work in a dedicated thread. For instructions on using this class, see:
 *          http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html Note that the API changed slightly in the
 *          3rd version: You must now invoke start() on the SwingWorker after creating it.
 */

public abstract class SwingWorker {
	private Object value; // see getValue(), setValue()

	// private Thread thread;

	/**
     * Class to maintain reference to current worker thread under separate synchronization control.
     */
	private static class ThreadVar {
		private Thread thread;

		ThreadVar(Thread t) {
			this.thread = t;
		}

		synchronized Thread get() {
			return this.thread;
		}

		synchronized void clear() {
			this.thread = null;
		}
	}

	ThreadVar threadVar;

	/**
     * Get the value produced by the worker thread, or null if it hasn't been constructed yet.
     */
	protected synchronized Object getValue() {
		return this.value;
	}

	/**
     * Set the value produced by worker thread
     */
	synchronized void setValue(Object x) {
		this.value = x;
	}

	/**
     * Compute the value to be returned by the <code>get</code> method.
     */
	public abstract Object construct();

	/**
     * Called on the event dispatching thread (not on the worker thread) after the <code>construct</code> method has
     * returned.
     */
	public void finished() {
	// NOP
	}

	/**
     * A new method that interrupts the worker thread. Call this method to force the worker to stop what it's doing.
     */
	public void interrupt() {
		Thread t = this.threadVar.get();
		if (t != null) {
			t.interrupt();
		}
		this.threadVar.clear();
	}

	/**
     * Return the value created by the <code>construct</code> method. Returns null if either the constructing thread
     * or the current thread was interrupted before a value was produced.
     * 
     * @return the value created by the <code>construct</code> method
     */
	public Object get() {
		while (true) {
			Thread t = this.threadVar.get();
			if (t == null) { return getValue(); }
			try {
				t.join();
			} catch (InterruptedException e) {
				Thread.currentThread().interrupt(); // propagate
				return null;
			}
		}
	}

	/**
     * Start a thread that will call the <code>construct</code> method and then exit.
     */
	public SwingWorker() {
		final Runnable doFinished = new Runnable() {
			@Override
            public void run() {
				finished();
			}
		};

		Runnable doConstruct = new Runnable() {
			@Override
            public void run() {
				try {
					setValue(construct());
				} finally {
					SwingWorker.this.threadVar.clear();
				}

				SwingUtilities.invokeLater(doFinished);
			}
		};

		Thread t = new Thread(doConstruct);
		this.threadVar = new ThreadVar(t);
	}

	/**
     * Start the worker thread.
     */
	public void start() {
		Thread t = this.threadVar.get();
		if (t != null) {
			t.start();
		}
	}
}
